/*!
    \copyright  Copyright (c) 2024 Qualcomm Technologies International, Ltd.
    All Rights Reserved.
    Qualcomm Technologies International, Ltd. Confidential and Proprietary.
    \file
    \addtogroup media_control_client
    \brief      Media control Profile OTS (Object Transfer Service) - Client Role Implementation.
    @{
*/

#include <logging.h>

#include "media_control_client_ots.h"
#include "media_control_client_private.h"
#include "media_control_client.h"

#ifdef INCLUDE_GATT_OTS_CLIENT_MODULE

#ifdef INCLUDE_LE_AUDIO_UNICAST
/* Move functions to KEEP section such that they are not removed during garbage collection */
#define MCP_OTS_CLIENT_KEEP UNITCODESECTION(KEEP)
#else
#define MCP_OTS_CLIENT_KEEP
#endif

void mediaControlClient_OtsConnect(media_control_client_instance_t *instance)
{
    media_control_client_gatt_device_data_t *stored_device_data;
    McpOtsHandles   ots_handles;

    /*read stored instance data from NVM*/
    stored_device_data = (media_control_client_gatt_device_data_t *) MediaControlClient_RetrieveClientHandles(instance->cid);

    /*check if ots handle in NVM is not null*/
    if (stored_device_data != NULL)
    {
        bool status;

        /* Create McpOtsHandles object for McpSetOtsHandles */
        if (stored_device_data->ots_dev_data.objSizeHandle != 0)
        {
            ots_handles.otsInstCount = 1;
            ots_handles.otsHandle = &stored_device_data->ots_dev_data;
        }
        else
        {
            ots_handles.otsInstCount = 0;
            ots_handles.otsHandle = NULL;
        }

        /*Set OTS handle*/
        status = McpSetOtsHandles(instance->mcp_profile_handle,instance->mcs_service_handle, &ots_handles);
        DEBUG_LOG_INFO("mediaControlClient_ots_connect: stored_dev_data: %p, status: %d", stored_device_data->ots_dev_data, status);
    }

    McpInitOptServicesReq(instance->mcp_profile_handle, MCP_OPTIONAL_SERVICE_OTS);
}

void mediaControlClient_GetOtsAttributeHandles(media_control_client_instance_t *instance, GattOtscDeviceData *ots_data)
{
    GattOtscDeviceData *ots_handles = McpGetOtsAttributeHandles((instance)->mcp_profile_handle, (instance)->mcs_service_handle);

    if (ots_handles != NULL)
    {
        *ots_data = *ots_handles;
        pfree(ots_handles);
    }
    else
    {
        memset(ots_data, 0, sizeof(GattOtscDeviceData));
    }
}

void mediaControlClient_HandleInitOptServicesCfmForOts(const McpInitOptServicesCfm *message)
{
    media_control_client_instance_t *instance;

    instance = MediaControlClient_GetInstance(media_client_compare_by_profile, message->prflHndl);

    if (instance != NULL && message->status == MCP_STATUS_SUCCESS)
    {
        for (uint8 service_scan = 0; service_scan < message->optServiceInfoCount; service_scan++)
        {
            DEBUG_LOG_INFO("mediaControlClient_HandleInitOptServicesCfm optService: 0x%02x, service_handle: 0x%04x",
                           message->optSerInfo[service_scan].optService, message->optSerInfo[service_scan].serviceHndl);
            if (message->optSerInfo[service_scan].optService & MCP_OPTIONAL_SERVICE_OTS)
            {
                instance->optional_services = message->optSerInfo[service_scan].optService;

                /* Retrieve mcp and ots device data from lib, and store it on NVM */
                mediaControlClient_WriteMediaDeviceDataToStore(instance);

                break;
            }
        }
    }

    DEBUG_LOG_INFO("mediaControlClient_HandleInitOptServicesCfm Received prfl handle: 0x%x, instance: %p, status: %d, count: %d",
                   message->prflHndl, instance, message->status, message->optServiceInfoCount);
}

MCP_OTS_CLIENT_KEEP
bool MediaControlClient_GetOtsObjectByObjectId(gatt_cid_t cid, MediaPlayerAttribute characType)
{
    media_control_client_instance_t *instance;

    instance = MediaControlClient_GetInstance(media_client_compare_by_cid, (unsigned)cid);

    if (instance != NULL && (instance->optional_services & MCP_OPTIONAL_SERVICE_OTS))
    {
        DEBUG_LOG_INFO("MediaControlClient_GetOtsObjectByObjectId: instance :%p, cid :0x%x, enum:MediaPlayerAttribute:0x%x",
                       instance, cid, characType);

        /* Request to get media player attribute */
        McpGetMediaPlayerAttribute(instance->mcp_profile_handle, instance->mcs_service_handle, characType);
        return TRUE;
    }
    return FALSE;
}

void mediaControlClient_HandleTrackObjectIdCfm(const McpGetObjectIdCfm *message)
{
    GattOtscObjectId  objectId;
    media_control_client_instance_t *instance;

    instance = MediaControlClient_GetInstance(media_client_compare_by_profile, message->prflHndl);

    if (instance != NULL)
    {
        if (message->status == MCP_STATUS_SUCCESS)
        {
            /* Read Object id of 6 bytes */
            for (uint8 objectIdIndex = 0; objectIdIndex < GATT_OTSC_OBJ_SIZE; objectIdIndex++)
            {
                objectId[objectIdIndex] = message->objectId[objectIdIndex];
            }

            /* Request object contents by Id */
            McpGetObjectContentsByObjIdReq(instance->mcp_profile_handle, instance->mcs_service_handle, objectId, TRUE, 0x0000, 0x00000000);
        }
        else if (media_control_taskdata.ots_callback_handler != NULL)
        {
            /* Report failure to application */
            media_control_client_ots_callback_msg_t ots_callback_msg;

            memset(&ots_callback_msg, 0, sizeof(media_control_client_ots_callback_msg_t));
            ots_callback_msg.cid = instance->cid;
            ots_callback_msg.id = message->id;
            ots_callback_msg.status = message->status;
            memcpy(&ots_callback_msg.objectId, &message->objectId, GATT_OTSC_OBJ_SIZE);
            media_control_taskdata.ots_callback_handler(ots_callback_msg);
        }
    }
    DEBUG_LOG_INFO("mediaControlClient_HandleTrackObjectIdCfm: Status: 0x%x, prflHndl: 0x%x, ObjectId received: 0x%x, enum:MCPMessageId:0x%x",
                   message->status, message->prflHndl, message->objectId, message->id);
}

void mediaControlClient_HandleObjContentsByObjectIdCfm(const McpGetObjectContentsByObjIdCfm *message)
{
    media_control_client_instance_t *instance;

    instance = MediaControlClient_GetInstance(media_client_compare_by_profile, message->prflHndl);

    if (instance != NULL && media_control_taskdata.ots_callback_handler != NULL)
    {
        /* Send received cid, message id, message status, data, data length, moreTocome to application */
        media_control_client_ots_callback_msg_t ots_callback_msg;

        memset(&ots_callback_msg, 0, sizeof(media_control_client_ots_callback_msg_t));
        ots_callback_msg.cid = instance->cid;
        ots_callback_msg.id = message->id;
        ots_callback_msg.status = message->status;
        ots_callback_msg.moreToCome = message->moreToCome;
        ots_callback_msg.data_length = message->dataLen;
        ots_callback_msg.data = message->data;
        media_control_taskdata.ots_callback_handler(ots_callback_msg);
    }
    DEBUG_LOG_INFO("mediaControlClient_HandleObjContentsByObjectIdCfm: Status: 0x%x, prflHndl: 0x%x, enum:MCPMessageId: 0x%x, resultsupplier: 0x%x, dataLen: %d",
                    message->status, message->prflHndl, message->id, message->resultSupplier, message->dataLen);
}

MCP_OTS_CLIENT_KEEP
bool MediaControlClient_McpStartDiscoverAllObjectReq(gatt_cid_t cid, McpObjectMetadataType objMetadataType)
{
    media_control_client_instance_t *instance;

    instance = MediaControlClient_GetInstance(media_client_compare_by_cid, (unsigned)cid);

    if (instance != NULL && (instance->optional_services & MCP_OPTIONAL_SERVICE_OTS))
    {
        DEBUG_LOG_INFO("MediaControlClient_McpStartDiscoverAllObjectReq: instance :%p, cid :0x%x, enum:McpObjectMetadataType: 0x%x",
                       instance, cid, objMetadataType);

        /* Request to start discover objects available in server */
        McpStartDiscoverAllObjReq(instance->mcp_profile_handle, instance->mcs_service_handle, objMetadataType,
                                   GATT_OTSC_FILTER_NONE, NULL, NULL, NULL, OTSC_INVALID_OBJECT_UUID);
        return TRUE;
    }
    return FALSE;
}

MCP_OTS_CLIENT_KEEP
bool MediaControlClient_McpStopDiscoverAllObjectReq(gatt_cid_t cid)
{
    media_control_client_instance_t *instance;

    instance = MediaControlClient_GetInstance(media_client_compare_by_cid, (unsigned)cid);

    if (instance != NULL && (instance->optional_services & MCP_OPTIONAL_SERVICE_OTS))
    {
        DEBUG_LOG_INFO("MediaControlClient_McpStopDiscoverAllObjectReq: instance :%p, cid :0x%x",instance, cid);

        /* Request to stop discover objects available in server */
        McpStopDiscoverObjectsReq(instance->mcp_profile_handle, instance->mcs_service_handle);
        return TRUE;
    }
    return FALSE;
}

void mediaControlClient_HandleDiscoverObjectCommonInd(const McpGetObjectMetadataCommonInd *message)
{
    media_control_client_instance_t *instance;

    instance = MediaControlClient_GetInstance(media_client_compare_by_profile, message->prflHndl);

    if (instance != NULL && media_control_taskdata.ots_callback_handler != NULL)
    {
        /* Send received cid, message id, message status, data, data length to application */
        media_control_client_ots_callback_msg_t ots_callback_msg;

        memset(&ots_callback_msg, 0, sizeof(media_control_client_ots_callback_msg_t));
        ots_callback_msg.cid = instance->cid;
        ots_callback_msg.id = message->id;
        ots_callback_msg.data = message->value;
        ots_callback_msg.data_length = message->valueLength;
        media_control_taskdata.ots_callback_handler(ots_callback_msg);
    }
    DEBUG_LOG_INFO("mediaControlClient_HandleDiscoverObjectCommonInd prflHndl: 0x%x, enum:McpMessageId: 0x%x, valueLength: %d",
                       message->prflHndl, message->id, message->valueLength);
}

void mediaControlClient_HandleDiscoverObjectCommonCfm(const McpDiscoverObjectCommonCfm *message)
{
    media_control_client_instance_t *instance;

    instance = MediaControlClient_GetInstance(media_client_compare_by_profile, message->prflHndl);

    if (instance != NULL && media_control_taskdata.ots_callback_handler != NULL)
    {
        /* inform application here about object discovery status */
        media_control_client_ots_callback_msg_t ots_callback_msg;

        memset(&ots_callback_msg, 0, sizeof(media_control_client_ots_callback_msg_t));
        ots_callback_msg.cid = instance->cid;
        ots_callback_msg.id = message->id;
        ots_callback_msg.status = message->status;
        media_control_taskdata.ots_callback_handler(ots_callback_msg);
    }
    DEBUG_LOG_INFO("mediaControlClient_HandleDiscoverObjectCommonCfm prflHndl: 0x%x enum:McpMessageId: 0x%x, status: 0x%x, resultSupplier: 0x%x",
                   message->prflHndl, message->id, message->status, message->resultSupplier);
}

MCP_OTS_CLIENT_KEEP
bool MediaControlClient_GetCurrentMediaObjectMetadata(gatt_cid_t cid, McpObjectMetadataType objMetadataType)
{
    media_control_client_instance_t *instance;

    instance = MediaControlClient_GetInstance(media_client_compare_by_cid, (unsigned)cid);

    if (instance != NULL && (instance->optional_services & MCP_OPTIONAL_SERVICE_OTS))
    {
        DEBUG_LOG_INFO("MediaControlClient_GetCurrentMediaObjectMetadata instance :%p, cid :0x%x, enum:McpObjectMetadataType:0x%x",
                       instance, cid, objMetadataType);
        McpGetCurrentMediaObjMetadataReq(instance->mcp_profile_handle, instance->mcs_service_handle, objMetadataType);
        return TRUE;
    }
    return FALSE;
}

void mediaControlClient_HandleCurrentMediaObjectMetadata(const McpGetObjectMetadataCommonCfm *message)
{
    media_control_client_instance_t *instance;

    instance = MediaControlClient_GetInstance(media_client_compare_by_profile, message->prflHndl);

    if (instance != NULL && media_control_taskdata.ots_callback_handler != NULL)
    {
        /* Send cid, message id, message status, data, data length to application */
        media_control_client_ots_callback_msg_t ots_callback_msg;

        memset(&ots_callback_msg, 0, sizeof(media_control_client_ots_callback_msg_t));
        ots_callback_msg.cid = instance->cid;
        ots_callback_msg.id = message->id;
        ots_callback_msg.status = message->status;
        ots_callback_msg.data_length = message->valueLength;
        ots_callback_msg.data = message->value;
        media_control_taskdata.ots_callback_handler(ots_callback_msg);
    }
    DEBUG_LOG_INFO("mediaControlClient_HandleCurrentMediaObjectMetadata prflHndl: 0x%x, enum:McpMessageId: 0x%x, status: 0x%x, valueLength: %d, resultSupplier: 0x%x",
                       message->prflHndl, message->id, message->status, message->valueLength, message->resultSupplier);
}

MCP_OTS_CLIENT_KEEP
bool MediaControlClient_RegisterForObjectChangedInd(gatt_cid_t cid, GattOtscCharacValuesMask charValsMask)
{
    media_control_client_instance_t *instance;

    instance = MediaControlClient_GetInstance(media_client_compare_by_cid, (unsigned)cid);

    if (instance != NULL && (instance->optional_services & MCP_OPTIONAL_SERVICE_OTS))
    {
        DEBUG_LOG_INFO("MediaControlClient_RegisterForObjectChangedInd instance :%p, cid :0x%x, GattOtscCharacValuesMask:0x%x",
                       instance, cid, charValsMask);
        McpRegisterForObjChangedIndReq(instance->mcp_profile_handle, instance->mcs_service_handle, charValsMask);
        return TRUE;
    }
    return FALSE;
}

void mediaControlClient_HandleOtsIndCfm(const McpOtsIndCfm* message)
{
    media_control_client_instance_t *instance;

    instance = MediaControlClient_GetInstance(media_client_compare_by_profile, message->prflHndl);

    /* inform application here about OTS object change indication register success or failure */
    if (instance != NULL && media_control_taskdata.ots_callback_handler != NULL)
    {
        /* Send cid, message id, status to application */
        media_control_client_ots_callback_msg_t ots_callback_msg;

        memset(&ots_callback_msg, 0, sizeof(media_control_client_ots_callback_msg_t));
        ots_callback_msg.cid = instance->cid;
        ots_callback_msg.id = message->id;
        ots_callback_msg.status = message->status;
        media_control_taskdata.ots_callback_handler(ots_callback_msg);
    }
    DEBUG_LOG_INFO("mediaControlClient_HandleOtsIndCfm prflHndl: 0x%x enum:McpMessageId: 0x%x, status: 0x%x, resultSupplier: 0x%x",
                   message->prflHndl, message->id, message->status, message->resultSupplier);
}

void mediaControlClient_HandleObjectChangedInd(const McpObjectChangedInd *message)
{
    media_control_client_instance_t *instance;

    instance = MediaControlClient_GetInstance(media_client_compare_by_profile, message->prflHndl);

    if (instance != NULL && media_control_taskdata.ots_callback_handler != NULL)
    {
        /* Send cid, message id, modifier, changeType, objectId to application */
        media_control_client_ots_callback_msg_t ots_callback_msg;

        memset(&ots_callback_msg, 0, sizeof(media_control_client_ots_callback_msg_t));
        ots_callback_msg.cid = instance->cid;
        ots_callback_msg.id = message->id;
        memcpy(&ots_callback_msg.objectId, &message->objectId, GATT_OTSC_OBJ_SIZE);
        ots_callback_msg.modifier = message->modifier;
        ots_callback_msg.changeType = message->changeType;
        media_control_taskdata.ots_callback_handler(ots_callback_msg);
    }
    DEBUG_LOG_INFO("mediaControlClient_HandleObjectChangedInd prflHndl: 0x%x enum:McpMessageId: 0x%x, objId: 0x%x, modifier: 0x%x, changeType: 0x%x",
                   message->prflHndl, message->id, message->objectId, message->modifier, message->changeType);
}

MCP_OTS_CLIENT_KEEP
bool MediaControlClient_McpAbortOtsProcedureReq(gatt_cid_t cid)
{
    media_control_client_instance_t *instance;

    instance = MediaControlClient_GetInstance(media_client_compare_by_cid, (unsigned)cid);

    if (instance != NULL && (instance->optional_services & MCP_OPTIONAL_SERVICE_OTS))
    {
        DEBUG_LOG_INFO("MediaControlClient_McpAbortOtsProcedureReq instance :%p, cid :0x%x",instance, cid);
        /* Send request to abort OTS procedure */
        McpAbortOtsProcedureReq(instance->mcp_profile_handle, instance->mcs_service_handle);
        return TRUE;
    }
    return FALSE;
}

MCP_OTS_CLIENT_KEEP
void MediaControlClient_RegisterOtsClientCallback(media_control_client_ots_callback_t handler)
{
    media_control_taskdata.ots_callback_handler = handler;

    DEBUG_LOG("MediaControlClient_RegisterOtsClientCallback: OTS client handle register");
}

#endif /* INCLUDE_GATT_OTS_CLIENT_MODULE */
